package mspbots.next.ticket.core.grader.interceptor;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
import mspbots.next.ticket.core.grader.AbstractGradeRuleAdapter;
import mspbots.next.ticket.core.grader.GradeRuleInterceptorRegistration;
import mspbots.next.ticket.core.points.PointResult;
import mspbots.next.ticket.utils.NumberUtil;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;


/**
 * com.mspbots.core.ticketai.NoneGradeRuleInterceptor
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/8/20
 */
@Log4j2
@Service(value = "STATUS_POINTS_GRADE_RULE")
public class StatusPointsGradeRuleInterceptor extends AbstractGradeRuleAdapter {

    private static final String RULE_KEY = "TicketStatus";
    private List<StatusPointsParams> params;

    @Override
    public Mono<GradeRuleInterceptorRegistration> prepare(GradeRuleInterceptorRegistration gradeRuleInterceptorRegistration) {
        return Flux.just()
                .doFinally(r -> this.params = super.objectMapper
                        .convertValue(gradeRuleInterceptorRegistration.getRule().getParams(), new TypeReference<>() {
                        }))
                .then(Mono.just(gradeRuleInterceptorRegistration));
    }

    @Override
    public Mono<PointResult> scoring(JsonNode ticket) {
        Integer statusId = super.getIntValueId(ticket.get("status"));
        LocalDateTime dateTimeValue;
        if (!ObjectUtils.isEmpty(ticket.get("auditor"))) {
            dateTimeValue = super.getDateTimeValue(ticket.get("auditor").get("time"));
        } else {
            dateTimeValue = super.getDateTimeValue(ticket.get("_info").get("lastUpdated"));
        }
        long totalMinutes = Duration.between(dateTimeValue, LocalDateTime.now()).toMinutes();
        int order = params.parallelStream()
                .filter(p -> p.getStatus() == statusId)
                .filter(StatusPointsParams::isSelected)
                .map(StatusPointsParams::getPoints)
                .map(statusPoints -> statusPoints.parallelStream().filter(point -> point.getTime() < totalMinutes)
                        .mapToInt(StatusPoints::getPoint).sum()).mapToInt(Integer::intValue).sum();
        String timeSum = NumberUtil.roundStr(totalMinutes / 60D, 2);
        return Mono.just(PointResult.withDefault().point(order)
                .detail(PointResult.Detail.with(order,
                        Map.of("statusName", super.getStringValueName(ticket.get("status")), "time", timeSum + " hours/"), RULE_KEY)));
    }

    @Data
    public static class StatusPointsParams {
        private List<StatusPoints> points;
        private int status;
        private boolean selected;
    }

    @Data
    private static class StatusPoints {
        private int time;
        private int point;
    }

}
